/**
    ATClab - scriptable Air Traffic Control simulations.
    Copyright (C) 2010 openatclab@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/

#include "uicomponent.h"

#include "atc.h"
#include "logger.h"
#include "presenter.h"

#include <qlabel.h>
#include <qlineedit.h>
#include <qpushbutton.h>
#include <qradiobutton.h>
#include <qtimer.h>
#include <qgroupbox.h>
//Added by qt3to4:
#include <Q3HBoxLayout>
#include <QKeyEvent>
#include <Q3GridLayout>
#include <Q3PtrList>
#include <Q3Frame>
#include <Q3VBoxLayout>
#include <Q3BoxLayout>
#include <QShowEvent>

#include <QButtonGroup>
#include <QPushButton>

using namespace pact;

////////////////////////////////////////////////////////////////////////////////////////////////////
//
// class BaseQuestion
//
////////////////////////////////////////////////////////////////////////////////////////////////////
//
//

/**
 *
 */
BaseQuestion::BaseQuestion( QWidget *parent, const std::string &name ) 
		: Question( parent, name ) 
		, _text()
		, _labels()
		, m_x(-1)
		, m_y(-1)
{
//	setFrameShape( QFrame::Box );
}

/**
 *
 */
BaseQuestion::~BaseQuestion() {}

//------------------------------------------------------------------------------
//
//

void BaseQuestion::set_text(const std::string&	text, int , int )  { _text = text; }

/**
 *
 */
void BaseQuestion::add_label( const std::string &text ) {
	_labels.push_back( text );
}


//------------------------------------------------------------------------------
// [protected] builders
//

/*!
 *
 */
QWidget* BaseQuestion::construct_text() {
	QLabel *text = new QLabel( _text.c_str(), this );

	if ((m_x != -1) && (m_y != -1))
	{
		text->move(m_x, m_y);
	}

	//text->setFrameShape( QFrame::Box );
	text->setWordWrap(true);
	text->setAlignment( Qt::AlignHCenter|Qt::AlignVCenter);
	
	return text;
}

/*!
 *
 */
QWidget* BaseQuestion::construct_labels( int *length ) {
	Q3Frame *frame = new Q3Frame( this );
//	frame->setFrameShape( QFrame::Box );

	if ( _labels.empty() ) return frame;

	QFontMetrics fm = fontMetrics();
	int len = 0;
	for ( LabelIt it = _labels.begin(); it != _labels.end(); ++it ) {

		QStringList sl = QStringList::split( "\n", it->c_str() );
		for ( QStringList::iterator sit = sl.begin(); sit != sl.end(); ++sit ) {
			int w = fm.width( *sit );
			len = w>len ? w : len;
		}
	}

	Q3BoxLayout *lblmgr = new Q3HBoxLayout( frame );
	for ( LabelIt lit = _labels.begin(); ; ) {
		QLabel *label = new QLabel( lit->c_str(), frame );
		//label->setFrameShape( QFrame::Box );
		//label->setFrameStyle( QFrame::Box|QFrame::Plain );
		label->setWordWrap(true);
		label->setAlignment( Qt::AlignHCenter|Qt::AlignVCenter );
		label->setFixedWidth( len );

		lblmgr->addWidget( label );

		if ( _labels.end() != ++lit ) 
				lblmgr->addStretch();
		else
				break;
	}

	if ( length ) *length = len;	// need to know the width of labels produced !!
	return frame;
}


//------------------------------------------------------------------------------
// [protected] virtual
//

/**
 * 
 */
void BaseQuestion::keyPressEvent( QKeyEvent *e ) { e->ignore(); }

/**
 * 
 */
void BaseQuestion::showEvent( QShowEvent * ) {  reset(); }


//------------------------------------------------------------------------------
// [protected] slots
//

/**
 * 
 */
void BaseQuestion::reset() {}

/**
 * 
 */
void BaseQuestion::validate() {}

/**
 * 
 */
void BaseQuestion::setAnswer( int idx ) {
	pact::Presenter* presenter = pact::Presenter::instance();
	pact::GlobalVariables globals = presenter->globals();
	pact::GlobalVariables::iterator it = globals.find(CHOICE_QUESTION_SELECTION);

	if (it != globals.end())
	{
		Global* glob = it->second;
		glob->set_value(idx);
	}

	logging::KVPairs kvs;
	kvs.push_back( std::make_pair( 
			std::string("qestion::setAnswer"), std::string(QString("%1").arg(idx)) 
		) );
	const QButtonGroup *bg = dynamic_cast<const QButtonGroup*>( sender() );
	kvs.push_back( std::make_pair( std::string(bg->name()), std::string(bg->button(idx)->text()) ) );
	emit sig_data( kvs );

	emit accepted();
}


////////////////////////////////////////////////////////////////////////////////////////////////////
//
// class ChoiceQuestion
//
////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
/**
 *
 */
ChoiceQuestion::ChoiceQuestion( QWidget *parent, const std::string &name ) 
		: BaseQuestion( parent, name ) 
		, _choices()
{}

/**
 *
 */
ChoiceQuestion::~ChoiceQuestion() {}

//------------------------------------------------------------------------------
// [public]
//

/**
 *
 */
void 
ChoiceQuestion::add_choice(	const	std::string &choice , 
							int		a_value, 
							const	std::string& a_ref, 
							int		x,
							int		y,
							int		w,
							int		h) 
{
	ChoiceDetails cd;

	cd.m_text = choice;
	cd.m_value = a_value;
	cd.m_ref = a_ref;
	cd.m_x = x;
	cd.m_y = y;
	cd.m_w = w;
	cd.m_h = h;

	_choices.push_back(cd);
}


//------------------------------------------------------------------------------
// [public] virtual
//

/**
 * 
 */
void ChoiceQuestion::construct() {

	Q3BoxLayout *mgr = new Q3VBoxLayout( this, 20 );
	mgr->addWidget( construct_text() );
	mgr->addSpacing( 20 );

	int ROWS = 2;
	int COLS = 3;

	Q3GridLayout *grid = new Q3GridLayout( mgr , ROWS, COLS, 0, 0 );

	int lblWidth = 0;
	int btnWidth = 0;
	QWidget* lblWidget = construct_labels(&lblWidth);
	QWidget* btnWidget = construct_buttons(&btnWidth);

	if ( lblWidth > btnWidth ) {
		grid->addMultiCellWidget( lblWidget, 0,0, 0,2 );
		grid->addWidget( btnWidget, 1,2 );
	}
	else {
		grid->addWidget( lblWidget, 0,2 );
		grid->addMultiCellWidget( btnWidget, 1,1, 0,2 );
	}
	
	int colSpacing = abs(lblWidth - btnWidth) / 2;
	grid->setColSpacing( 0, colSpacing );
	grid->setColSpacing( 2, colSpacing );
}


//------------------------------------------------------------------------------
// [private]
//

/**
 *
 */
QWidget* ChoiceQuestion::construct_buttons( int *) {
	QGroupBox *btnbox = new QGroupBox( this );
	QVBoxLayout* layout = new QVBoxLayout();
	QButtonGroup* btnmgr = new QButtonGroup(this);
	btnmgr->setObjectName( this->name() );
	
	for (ChoiceIt cit = _choices.begin(); cit != _choices.end(); ++cit) {
		QPushButton *btn = new QPushButton(cit->c_str(), btnbox);
		layout->addWidget(btn);
		btnmgr->addButton(btn, cit->m_value);
	}

	btnbox->setLayout(layout);
	connect( btnmgr, SIGNAL(buttonClicked(int)), this, SLOT(setAnswer(int)) );
	
	return btnbox;
}

void 
ChoiceQuestion::setAnswer(int a_idx)
{
	for (ChoiceIt cit = _choices.begin(); cit != _choices.end(); ++cit) 
	{
		if ((*cit).m_value == a_idx)
		{
			pact::GlobalVariables::iterator it = 
			pact::Presenter::instance()->globals().find((*cit).m_ref);

			if (it != pact::Presenter::instance()->globals().end())
			{
				it->second->set_value(a_idx);
			}


			// workaround: do not set globalidx if choice_question's name ends with _nolink.
			if (std::string(this->name()).rfind("_nolink") == std::string::npos)	{
				pact::Presenter::instance()->SetGlobalIdx((*cit).m_ref);
			}
		}
	}

	BaseQuestion::setAnswer(a_idx);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// class BaseScaleQuestion
//
////////////////////////////////////////////////////////////////////////////////////////////////////
//
//

/**
 *
 */
BaseScaleQuestion::BaseScaleQuestion( QWidget *parent, const std::string &name ) 
		: BaseQuestion( parent, name ) 
		, _scaleMin( 0 )
		, _scaleMax( 0 )
{}

/**
 *
 */
BaseScaleQuestion::~BaseScaleQuestion() {}


//------------------------------------------------------------------------------
//
//

/**
 *
 */
void BaseScaleQuestion::set_scale( int min, int max ) {
	_scaleMin = min;
	_scaleMax = max;
}


//------------------------------------------------------------------------------
//
//

/**
 *
 */

QWidget* BaseScaleQuestion::construct_scale(int *width) {
    QGroupBox* btnbox = new QGroupBox(this);
    QBoxLayout* layout = new QHBoxLayout();
    QButtonGroup* btnmgr = new QButtonGroup(this);
    btnmgr->setObjectName(this->objectName());
    btnmgr->setExclusive(false);
    int id = 0;

    for (int scale = _scaleMin; scale <= _scaleMax; ++scale) {
        QAbstractButton *btn = new QPushButton(QString("%1").arg(scale), btnbox);
        btn->setFocusPolicy(Qt::NoFocus);
        btn->setFixedWidth(atc::SCALE_BUTTON_WIDTH);
        layout->addWidget(btn);
        btnmgr->addButton(btn, id++);
    }

    btnbox->setLayout(layout);
    connect(btnmgr, SIGNAL(buttonClicked(int)), this, SLOT(setAnswer(int)));

    if (width) *width = btnmgr->button(0)->width();

    return btnbox;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// class ScaleQuestion
//
////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
/**
 *
 */
ScaleQuestion::ScaleQuestion( QWidget *parent, const std::string &name ) 
		: BaseScaleQuestion( parent, name ) 
{}

/**
 *
 */
ScaleQuestion::~ScaleQuestion() {}

//------------------------------------------------------------------------------
//
//

/**
 * 
 */
void ScaleQuestion::construct() {
	Q3BoxLayout *mgr = new Q3VBoxLayout( this, 20 );
	mgr->addWidget( construct_text() );
	//mgr->addSpacing( 20 );

	int ROWS = 2;
	int COLS = 3;

	Q3GridLayout *grid = new Q3GridLayout( mgr , ROWS, COLS, 0, 0 );

	int lblWidth = 0;
	int btnWidth = 0;
	QWidget* lblWidget = construct_labels(&lblWidth);
	QWidget* btnWidget = construct_scale(&btnWidth);

	if ( lblWidth > btnWidth ) {
		grid->addMultiCellWidget( lblWidget, 0,0, 0,2 );
		grid->addWidget( btnWidget, 1,1 );
	}
	else {
		grid->addWidget( lblWidget, 0,1 );
		grid->addMultiCellWidget( btnWidget, 1,1, 0,2 );
	}
	
	int colSpacing = abs(lblWidth - btnWidth) / 2;
	grid->setColSpacing( 0, colSpacing );
	grid->setColSpacing( 2, colSpacing );
}


////////////////////////////////////////////////////////////////////////////////////////////////////
//
// class MultiQuestion
//
////////////////////////////////////////////////////////////////////////////////////////////////////
//
//

/**
 *
 */
MultiQuestion::MultiQuestion( QWidget *parent, const std::string &name ) 
		: BaseScaleQuestion( parent, name ) 
		, _questions()
		, _isNumbered( false )
		, _mustAnswer( false )
		, _answers()
		, _okBtn( 0 )
{}

/**
 *
 */
MultiQuestion::~MultiQuestion() {}

//------------------------------------------------------------------------------
//
//

/**
 *
 */
void MultiQuestion::add_question( const std::string &question ) {
	_questions.push_back( question );
}

/**
 *
 */
void MultiQuestion::set_numbered() { _isNumbered = true; }

/**
 *
 */
void MultiQuestion::set_must_answer() { _mustAnswer = true; }

//------------------------------------------------------------------------------
// [protected] slots
//

/**
 * Restore clean default state
 */
void MultiQuestion::reset() {

	for ( AnswerIt ait = _answers.begin(); ait != _answers.end(); ++ait ) {
		QAbstractButton *btn = (*ait)->checkedButton();
		if ( btn )
				dynamic_cast< QRadioButton * >(btn)->setChecked( false );
	}

	_okBtn->setEnabled( !_mustAnswer );
}

/**
 *
 */
void MultiQuestion::validate() {

	const QButtonGroup *bg = dynamic_cast<const QButtonGroup*>( sender() );
	logging::KVPairs kvs;
	kvs.push_back( std::make_pair(std::string("action"),std::string("validate")) );
	kvs.push_back( std::make_pair(std::string("multi_question"), name()) );
	kvs.push_back( std::make_pair(std::string(bg->name()), std::string(bg->checkedButton()->name())) );

	emit sig_data( kvs );

	foreach(QButtonGroup *answer, _answers)
			if ( !answer->checkedButton() ) return;

	_okBtn->setEnabled( true );
}


//------------------------------------------------------------------------------
// [protected] virtual
//

/**
 * 
 */
void MultiQuestion::keyPressEvent( QKeyEvent *e ) {
	
	if      ( Qt::Key_Escape == e->key() ) reset();
	else if ( Qt::Key_Return == e->key() ) _okBtn->animateClick();

	e->ignore();

}

//------------------------------------------------------------------------------
//
//

/**
 * 
 */
void MultiQuestion::construct() {

	Q3BoxLayout *mgr = new Q3VBoxLayout( this, 20 );
	mgr->addWidget( construct_text() );
	mgr->addSpacing( 10 );

	int ROW_ADJUSTMENT = 2;
	int ROWS = _questions.size() + ROW_ADJUSTMENT;	// +1 descriptive label; +1 number labels
	
	int SCALES = _scaleMax - _scaleMin + 1; // inclusive

//	int NUMBER_COL   = 0;
	int QUESTION_COL = _isNumbered ? 1 : 0;
	int PRE_COL_0    = QUESTION_COL + 1;
	int SCALE_COL_0  = PRE_COL_0 + 1;
	int SCALE_COL_N  = SCALE_COL_0 + SCALES - 1;
	int POST_COL_N   = SCALE_COL_N + 1;

	int COLS = POST_COL_N + 1;

	int DIGIT_WIDTH = this->fontMetrics().width( "0" );

	Q3GridLayout *grid = new Q3GridLayout( mgr , ROWS, COLS, 0, 0 );
	int lblWidth = 0;
	grid->addMultiCellWidget( construct_labels(&lblWidth), 0,0, PRE_COL_0, POST_COL_N );

	int colSpace = (lblWidth - atc::RADIO_LABEL_WIDTH) / 2;
	grid->setColSpacing( PRE_COL_0, colSpace );
	grid->setColSpacing( POST_COL_N, colSpace );


	Q3PtrList<QLabel> labelList;
	int len = 0;
	for ( int n = 0, scale = _scaleMin; scale <= _scaleMax; ++scale, ++n ) {
		QString caption = QString( QString("%1").arg(scale) );
		if ( caption.length() > len ) len = caption.length();
		QLabel *lbl = new QLabel( caption, this  );
		lbl->setAlignment( Qt::AlignCenter );
//		lbl->setFrameShape( QFrame::Box );
		lbl->setMargin( 5 );
		//lbl->setPaletteBackgroundColor( Qt::yellow );
		grid->addWidget( lbl, 1, SCALE_COL_0 + n );
		labelList.append( lbl );
	}

	//++len;
	len *= DIGIT_WIDTH;
	for ( QLabel *lbl = labelList.first(); lbl; lbl = labelList.next() ) {
		lbl->setFixedWidth( atc::RADIO_LABEL_WIDTH );
	}

    for (unsigned int i = 0; i < _questions.size(); ++i ) {

		int row = i + ROW_ADJUSTMENT;

		QColor bgcolor = i % 2 ? Qt::gray : Qt::darkGray;

		Q3Frame *bg = new Q3Frame( this );
		
		QPalette p;
		p.setColor(bg->backgroundRole(), bgcolor);
		bg->setPalette(p);
		bg->setAutoFillBackground(true);

		grid->addMultiCellWidget( bg, row, row, 0, SCALE_COL_N );

		if ( _isNumbered ) {
			QLabel *number = new QLabel( QString("%1)").arg(i+1), this );
			number->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
			number->setMargin( 10 );
			number->setPaletteBackgroundColor( bgcolor );
			grid->addWidget( number, row, 0 );
		}

		QLabel* question = new QLabel( _questions[i].c_str(), this );
		question->setPaletteBackgroundColor( bgcolor );
		question->setMargin( 10 );
		grid->addWidget( question, row, QUESTION_COL );

		QButtonGroup *btngrp = new QButtonGroup( this );
		btngrp->setObjectName(_questions[i].c_str());
		_answers.push_back( btngrp );

		connect( btngrp, SIGNAL( buttonClicked(int) ), this, SLOT( validate() ) );

		for ( int n = 0, scale = _scaleMin; scale <= _scaleMax; ++scale, ++n ) {
			Q3Frame *btnFrame = new Q3Frame( this );
//			btnFrame->setFrameShape( QFrame::Box );
			btnFrame->setPaletteBackgroundColor( bgcolor );
			
			Q3BoxLayout *rbmgr = new Q3HBoxLayout( btnFrame );
			rbmgr->addStretch();
			QRadioButton *btn = new QRadioButton( btnFrame, QString("%1").arg(scale) );
			btn->setPaletteBackgroundColor( bgcolor );
			rbmgr->addWidget( btn );
			rbmgr->addStretch();

			btngrp->addButton(btn);

			grid->addWidget( btnFrame, row, SCALE_COL_0 + n );
		}
	}

	Q3BoxLayout *okmgr = new Q3HBoxLayout( mgr );
	okmgr->addStretch();
	_okBtn = new QPushButton( "OK", this );
	okmgr->addWidget( _okBtn );

	connect( _okBtn, SIGNAL( clicked() ), this, SLOT( done() ) );
}

/**
 * 
 */
void MultiQuestion::done() {
	logging::KVPairs kvs;
	kvs.push_back( std::make_pair(std::string("action"),std::string("done")) );
	kvs.push_back( std::make_pair(std::string("multi_question"), name()) );

	for ( AnswerIt ait = _answers.begin(); ait != _answers.end(); ++ait ) {
		QAbstractButton *btn = (*ait)->checkedButton();
		std::string select( btn ? btn->name() : "NO_SELECTION" );
		kvs.push_back( std::make_pair(std::string((*ait)->name()), select) );
	}
	emit sig_data( kvs );
	emit accepted();

}


////////////////////////////////////////////////////////////////////////////////////////////////////
//
// class ValidatedQuestion
//
////////////////////////////////////////////////////////////////////////////////////////////////////
//
//

ValidatedQuestion::ValidatedQuestion( QWidget *p, const std::string &name, ValidationQuestionType a_type)
		: Question( p, name )
		, _input( new QLineEdit(this) )
		, _message( new QLabel(this) )
		, _okBtn( new QPushButton("OK", this) )
		, m_column(0)
		, m_type(a_type)
{
	m_layout = new Q3GridLayout( this, 3, 3, 20 );
	m_layout->addMultiCellWidget( _message, 0, 0, 0, 2 );

	m_layout->addWidget( _okBtn, 2, 1 );

	connect( _okBtn, SIGNAL( clicked() ), this, SLOT( validate() ) );
}

ValidatedQuestion::~ValidatedQuestion() {}

//------------------------------------------------------------------------------
//
//

void ValidatedQuestion::set_text( const std::string &text ) {
	QLabel* label = new QLabel(text.c_str(), this);
	label->setAutoFillBackground(true);
	m_layout->addWidget(label, 1, m_column++);
}

void ValidatedQuestion::set_validator( QValidator *v ) {
	_input->setValidator( v );

	m_layout->addMultiCellWidget(_input, 1, 1, m_column, m_column + 1);
	m_column++;
}

void ValidatedQuestion::set_validator_message( const std::string &mesg ) {
	_message->setText( mesg.c_str() );
}

//------------------------------------------------------------------------------
// [protected] virtual
//

/**
 * 
 */
void ValidatedQuestion::keyPressEvent( QKeyEvent *e ) { e->ignore(); }

/**
 * 
 */
void ValidatedQuestion::showEvent(QShowEvent *) { reset(); }


//------------------------------------------------------------------------------
// [protected] slots
//

	
void ValidatedQuestion::reset() {
	_message->setPaletteForegroundColor( _message->paletteBackgroundColor() );
	_input->clear();
}

void ValidatedQuestion::validate() {
	logging::KVPairs kvs;

	kvs.push_back( std::make_pair( name(), _input->text().latin1() ) );

	if ( ! _input->hasAcceptableInput() ) {
		_message->setPaletteForegroundColor( Qt::red );
		kvs.push_back( std::make_pair( std::string("validation"), std::string("rejected") ) );
	}

	else {
		kvs.push_back( std::make_pair( std::string("validation"), std::string("accepted") ) );
		QTimer::singleShot( 0, this, SLOT(done()) );
	}

	if (m_type != VALIDATION_NORMAL)
	{
		int choiceSelection = 0;

		pact::GlobalVariables::iterator it = 
			pact::Presenter::instance()->globals().find(CHOICE_QUESTION_SELECTION);

		if (it != pact::Presenter::instance()->globals().end())
		{
			choiceSelection = it->second->value();
		}

		std::string label = "";
		std::string val = "";

		switch (m_type)
		{
		case VALIDATION_SR_GOAL:
			label = "srgoal";
			it = pact::Presenter::instance()->globals().find(SR_GOAL);
			break;
		case VALIDATION_NORM_GOAL:
			label = "ngoal";
			it = pact::Presenter::instance()->globals().find(NORM_GOAL);
            break;
        default:
            ;
		}
	
		if (choiceSelection == 2)
		{
			val = "-" + std::string(_input->text().latin1());
			
			if (it != pact::Presenter::instance()->globals().end())
			{
				it->second->set_value(-1 * atoi(_input->text().latin1()));
			}
		}
		else
		{
			val = _input->text().latin1();
			if (it != pact::Presenter::instance()->globals().end())
			{
				it->second->set_value(atoi(_input->text().latin1()));
			}
		}

		kvs.push_back(std::make_pair(label, val));
	}

	emit sig_data( kvs );
}

void ValidatedQuestion::done() {
	emit accepted();
	emit sig_accepted( _input->text().toInt() );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
